﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Universe.MediaProbe.Utils
{
    using System.IO;
    using System.Reflection;
    using System.Xml;
    using System.Xml.Xsl;

    public class XmlUtils
    {
        static readonly private string Xml2Html;

        static XmlUtils()
        {
            var type = typeof(XmlUtils);
            var s = Assembly.GetExecutingAssembly().GetManifestResourceStream(type, "xml2html.xslt");
            using (s)
            {
                if (s == null)
                    throw new Exception("Embedded xml2html.xslt is expected at " + type.Namespace);

                using (StreamReader r = new StreamReader(s))
                    Xml2Html = r.ReadToEnd();
            }
        }

        public static string GetXmlAsHtml(byte[] xml)
        {
            if (xml == null)
                throw new ArgumentNullException("xml");

            var mem = new MemoryStream(xml);
            XmlTextReader tr = new XmlTextReader(mem);
            return GetXmlAsHtml(tr);
        }

        public static string GetXmlAsHtml(XmlReader xmlReader)
        {
            if (xmlReader == null)
                throw new ArgumentNullException("xmlReader");

            StringBuilder ret = new StringBuilder();
            StringWriter wr1 = new StringWriter(ret);
            XmlTextWriter wr2 = new XmlTextWriter(wr1);

            XslCompiledTransform xsl = new XslCompiledTransform();
            XmlTextReader xslReader = new XmlTextReader(new StringReader(Xml2Html));
            xsl.Load(xslReader);
            xsl.Transform(xmlReader, wr2);

            return ret.ToString();
        }

        public static bool SafeLoad(byte[] xml, Encoding encoding, out XmlDocument doc, out Exception exception)
        {
            exception = null;
            try
            {
                // 1st load - suppose illegal characters are absent
                TextReader tr = new StreamReader(new MemoryStream(xml), encoding);
                var text = tr.ReadToEnd();
                doc = new XmlDocument();
                doc.LoadXml(text);
            }
            catch (Exception ex)
            {
                exception = ex;
                doc = null;
            }

            if (doc == null)
            {
                try
                {
                    // 2nd: try to load wothout illegal chars
                    TextReader tr = new StreamReader(new MemoryStream(xml), encoding);
                    var text = tr.ReadToEnd();
                    doc = new XmlDocument();
                    doc.LoadXml(XmlUtils.TrimIllegaslCharacters(text));
                }
                catch
                {
                    doc = null;
                }
            }

            return doc != null;
        }
        
        public static string TrimIllegaslCharacters(string xmlDoc)
        {
            if (xmlDoc == null)
                throw new ArgumentNullException("xmlDoc");

            StringBuilder ret = new StringBuilder();
            foreach (var ch in xmlDoc)
            {
                if (IsLegalXmlChar(ch))
                    ret.Append(ch);
            }

            return ret.ToString();
        }

        public static bool IsLegalXmlChar(int character)
        {
            return
                (
                    character == 0x9 /* == '\t' == 9   */ ||
                    character == 0xA /* == '\n' == 10  */ ||
                    character == 0xD /* == '\r' == 13  */ ||
                    (character >= 0x20 && character <= 0xD7FF) ||
                    (character >= 0xE000 && character <= 0xFFFD) ||
                    (character >= 0x10000 && character <= 0x10FFFF)
                );
        }

    }
}
